package cn.imatu.framework.log;

import cn.hutool.http.GlobalInterceptor;
import cn.imatu.framework.core.response.*;
import cn.imatu.framework.log.annotation.*;
import cn.imatu.framework.log.common.*;
import cn.imatu.framework.log.core.customizer.*;
import cn.imatu.framework.log.core.filter.*;
import cn.imatu.framework.log.integration.hutoolhttp.*;
import cn.imatu.framework.log.integration.resttemplate.*;
import cn.imatu.framework.tool.core.*;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.TtlMDCAdapter;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;

import javax.annotation.PostConstruct;
import java.util.Objects;
import java.util.Optional;

/**
 * @author shenguangyang
 */
@Slf4j
@AutoConfiguration
@Import({
        LogRestTemplateInterceptor.class, MdcFieldCustomizerConfig.class, LyAccessLogAspect.class,
        LogRunner.class
})
@EnableConfigurationProperties(LyLogProperties.class)
public class LyLogAutoConfiguration {

    @PostConstruct
    @SuppressWarnings("all")
    public void init() {
        TtlMDCAdapter.init();
        Resp.setTraceIdKey(LogCons.TRACE_ID);

        // hutool
        GlobalInterceptor.INSTANCE.addRequestInterceptor(new LyLogHutoolHttpInterceptor());

        log.info("init {}", this.getClass().getName());
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnWebApplication(type = Type.SERVLET)
    public OperateLogAnnotScan operateLogAnnotScan() {
        return new OperateLogAnnotScan();
    }

    @Bean
    @ConditionalOnMissingBean(AccessLogConfig.class)
    public AccessLogConfig accessLogConfig() {
        return AccessLogConfig.builder()
            .defaultOpsType("1").defaultEventType("default")
            .reqLogHandle((info) -> {
                OperateLog operateLogAnnot = info.getOperateLogAnnot();
                Boolean isPrintIn = Optional.ofNullable(operateLogAnnot).map(OperateLog::isPrintIn).orElse(true);
                Object reqData = info.getReqData();

                String printfInfo = String.format("%s:%s(%s ms)", info.getUrl(), info.getHttpMethod(), info.getCostTime());
                if (isPrintIn && Objects.nonNull(reqData)) {
                    String reqDataStr = JSON.toJSONString(reqData);
                    reqDataStr = reqDataStr.length() > 2000 ? reqDataStr.substring(0, 2000) + " ......" : reqDataStr;
                    printfInfo = " --- " + reqDataStr;
                }
                if (info.getStatus() == BizStatus.OK) {
                    log.info("request => {}", printfInfo);
                } else {
                    log.error("request => {}", printfInfo);
                }

            }).respLogHandle((info) -> {
                OperateLog operateLogAnnot = info.getOperateLogAnnot();
                Boolean isPrintOut = Optional.ofNullable(operateLogAnnot).map(OperateLog::isPrintOut).orElse(true);
                Object respData = info.getRespData();
                String errorMsg = info.getErrorMsg();

                Object printfRespData = respData instanceof Resp ? ((Resp<?>) respData).getData() : respData;

                if (StringUtils.isNotEmpty(errorMsg)) {
                    String newErrorMsg = errorMsg.length() > 4000 ? errorMsg.substring(0, 4000) + " ......" : errorMsg;
                    log.error("response => {}:{} --- {}", info.getUrl(), info.getHttpMethod(), newErrorMsg);
                }

                String respDataStr = "";
                if (isPrintOut && Objects.nonNull(printfRespData)) {
                    respDataStr = JSON.toJSONString(printfRespData);
                    respDataStr = respDataStr.length() > 4000 ? respDataStr.substring(0, 4000) + " ......" : respDataStr;
                }
                if (info.getStatus() == BizStatus.OK) {
                    log.info("response => {}:{} --- {}", info.getUrl(), info.getHttpMethod(), respDataStr);
                } else {
                    log.error("response => {}:{} --- {}", info.getUrl(), info.getHttpMethod(), respDataStr);
                }
            }).build();
    }



    @Bean
    @ConditionalOnMissingBean(CustomTraceUser.class)
    public CustomTraceUser defaultCustomTraceUser() {
        return () -> null;
    }
}
